using System.Collections.Generic;
using FireStarter.Base;
using FireStarter.CodeGeneration;
using FireStarter.CodeGeneration.Generator;
using FireStarterModeller.BO;
using NUnit.Framework;

namespace FireStarter.Test.CodeGeneration.Generator
{
    [TestFixture]
    public class TestIfExpression : TestBase
    {
        [Test]
        public void Test_Creation()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestBoolean):
            //		test; 
            //
            //|>>
            const string booleanPropName = "TestBoolean";
            IfExpression ifExpression = new IfExpression(booleanPropName);
            LiteralExpression literalExpression = new LiteralExpression(" test; \n");
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            ifExpression.Part = literalExpression;
            //---------------Test Result -----------------------
            Assert.AreSame(literalExpression, ifExpression.Part);
            Assert.IsNull(ifExpression.Next);
            Assert.AreEqual(booleanPropName, ifExpression.PropertyName);
        }

        [Test]
        public void Test_Generation_BooleanIsTrue()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestBoolean):
            //test; |>>
            const string booleanPropName = "TestBoolean";
            IfExpression ifExpression = new IfExpression(booleanPropName);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(booleanPropName, "true");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Generation_BooleanIsFalse()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestBoolean):
            //test; |>>
            const string booleanPropName = "TestBoolean";
            IfExpression ifExpression = new IfExpression(booleanPropName);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(booleanPropName, "false");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }

        [Test]
        public void Test_Generation_ThrowsExceptionIfBooleanCastFails()
        {
            const string booleanPropName = "TestBoolean";
            IfExpression ifExpression = new IfExpression(booleanPropName);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(booleanPropName, "invalidvalue");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            try
            {
                string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
                Assert.Fail("Failed boolean conversion should throw exception");
            }
            catch(GenerationException ex)
            {
                const string expectedMessage = "During generation of an IF statement, the property 'TestBoolean' did not return a value that could be cast to a boolean.";
                StringAssert.AreEqualIgnoringCase(expectedMessage, ex.Message);
            }
        }

        [Test]
        public void Test_Generation_WithComplexPart()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestBoolean):
            //<<|=PropName|>>;
            //|>>

            const string booleanPropName = "TestBoolean";
            const string literal = ";\n";
            const string propname = "PropName";
            const string propnameValue = "PropNameValue";
            ValueExpression valueExpression = new ValueExpression(propname);

            IfExpression ifExpression = new IfExpression(booleanPropName);
            LiteralExpression literalExpression = new LiteralExpression(literal);
            valueExpression.Next = literalExpression;
            ifExpression.Part = valueExpression;

            var strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(booleanPropName, "true");
            strategyStub.SetValue(propname, propnameValue);

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(IModelElement), strategyStub);

            IModelClass dmClass = new DMClass();
            //---------------Execute Test ----------------------

            string result = ifExpression.Generate(modelStrategyMapper, dmClass);

            //---------------Test Result -----------------------
            Assert.AreEqual(propnameValue + literal, result);
            Assert.AreSame(dmClass, strategyStub.ModelElement);
        }

        [Test]
        public void Test_NotReversesBoolean()
        {
            //---------------Set up test pack-------------------
            //<<|IF(!TestBoolean):
            //test; |>>
            const string booleanPropName = "TestBoolean";
            string booleanPropName_WithNot = "!" + booleanPropName;
            IfExpression ifExpression = new IfExpression(booleanPropName_WithNot);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(booleanPropName, "false");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Equals_ReturnsTrue_ValueEqualsLiteral()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS "Hello"):
            //test; |>>
            const string stringPropName = "TestString";
            string fullClause = stringPropName + @" EQUALS ""Hello""";
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Equals_ReturnsFalse_ValueNotEqualsLiteral()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS "Hello"):
            //test; |>>
            const string stringPropName = "TestString";
            string fullClause = stringPropName + @" EQUALS ""Hello""";
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello world");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }

        [Test]
        public void Test_Equals_ReturnsTrue_ValueEqualsValue()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS OtherString):
            //test; |>>
            const string stringPropName = "TestString";
            const string stringPropNameRight = "OtherString";
            string fullClause = stringPropName + " EQUALS " + stringPropNameRight;
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello");
            strategyStub.SetValue(stringPropNameRight, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Equals_ReturnsFalse_ValueNotEqualsValue()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS OtherString):
            //test; |>>
            const string stringPropName = "TestString";
            const string stringPropNameRight = "OtherString";
            string fullClause = stringPropName + " EQUALS " + stringPropNameRight;
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello world");
            strategyStub.SetValue(stringPropNameRight, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }

        [Test]
        public void Test_Equals_ReturnsTrue_LiteralEqualsValue()
        {
            //---------------Set up test pack-------------------
            //<<|IF("Hello" EQUALS TestString):
            //test; |>>
            const string stringPropName = "TestString";
            string fullClause = @"""Hello"" EQUALS " + stringPropName;
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Equals_ReturnsFalse_LiteralNotEqualsValue()
        {
            //---------------Set up test pack-------------------
            //<<|IF("Hello" EQUALS TestString):
            //test; |>>
            const string stringPropName = "TestString";
            string fullClause = @"""Hello"" EQUALS " + stringPropName;
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello world");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }

        [Test]
        public void Test_Equals_ReturnsTrue_ValueEqualsValueEqualsLiteral()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS "Hello"):
            //test; |>>
            const string stringPropName = "TestString";
            const string stringPropName2 = "TestString2";
            string fullClause = string.Format(@"{0} EQUALS {1} EQUALS ""Hello""", stringPropName, stringPropName2);
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello");
            strategyStub.SetValue(stringPropName2, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual(literal, result);
        }

        [Test]
        public void Test_Equals_ReturnsFalse_ValueNotEqualsValueEqualsLiteral()
        {
            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS "Hello"):
            //test; |>>
            const string stringPropName = "TestString";
            const string stringPropName2 = "TestString2";
            string fullClause = string.Format(@"{0} EQUALS {1} EQUALS ""Hello""", stringPropName, stringPropName2);
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello world");
            strategyStub.SetValue(stringPropName2, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }

        [Test]
        public void Test_EqualsNull_ReturnsFalseForNotNullValue()
        {

            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS NULL):  |>>
            const string stringPropName = "TestString";
            string fullClause = string.Format(@"{0} EQUALS NULL", stringPropName);
            IfExpression ifExpression = new IfExpression(fullClause);

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello world");
            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);

            //---------------Execute Test ----------------------
            bool result = ifExpression.EvaluateExpression(modelStrategyMapper, new ModelElementStub());
            
            //---------------Test Result -----------------------
            Assert.IsFalse(result);   
        }

        [Test]
        public void Test_EqualsNull_ReturnsTrueForNullValue()
        {

            //---------------Set up test pack-------------------
            //<<|IF(TestString EQUALS NULL):  |>>
            const string stringPropName = "TestString";
            string fullClause = string.Format(@"{0} EQUALS NULL", stringPropName);
            IfExpression ifExpression = new IfExpression(fullClause);

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, null);
            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);

            //---------------Execute Test ----------------------
            bool result = ifExpression.EvaluateExpression(modelStrategyMapper, new ModelElementStub());

            //---------------Test Result -----------------------
            Assert.IsTrue(result);
        }

        [Test]
        public void Test_RelatedPropertyInIf()
        {
            //---------------Set up test pack-------------------
            //<<|IF(Parent.StubValue EQUALS "hello"):  |>>
            const string stringPropName = "Parent.StubValue";
            string hello = "hello";
            string fullClause = string.Format(@"{0} EQUALS ""{1}""", stringPropName, hello);
            IfExpression ifExpression = new IfExpression(fullClause);

            ModelElementStub elementStub = new ModelElementStub();
            ModelElementStub parentElementStub = new ModelElementStub();
            
            parentElementStub.StubValue = hello;
            elementStub.Parent = parentElementStub;

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), new ReflectionGenerationStrategy());

            //---------------Execute Test ----------------------
            bool result = ifExpression.EvaluateExpression(modelStrategyMapper, elementStub);
            
            //---------------Test Result -----------------------
            Assert.IsTrue(result);  
        }

        [Test]
        public void Test_Equals_NotReversesBoolean()
        {
            //---------------Set up test pack-------------------
            //<<|IF(!TestString EQUALS "Hello"):
            //test; |>>
            const string stringPropName = "TestString";
            const string fullClause = "!" + stringPropName + @" EQUALS ""Hello""";
            IfExpression ifExpression = new IfExpression(fullClause);
            const string literal = "test; ";
            LiteralExpression literalExpression = new LiteralExpression(literal);
            ifExpression.Part = literalExpression;

            GenerationStrategyStub strategyStub = new GenerationStrategyStub();
            strategyStub.SetValue(stringPropName, "Hello");

            var modelStrategyMapper = new ModelStrategyMapper();
            modelStrategyMapper.SetStrategy(typeof(ModelElementStub), strategyStub);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            string result = ifExpression.Generate(modelStrategyMapper, new ModelElementStub());
            //---------------Test Result -----------------------
            Assert.AreEqual("", result);
        }
    }
}